#pragma once
#include "matrix2.h"


BEGIN_NAMESPACE_OXGE

template <typename T> 
class MATHLIB_TRANSFER Matrix3T
{
public:
	typedef Matrix3T<T> MatrixType;
	typedef Matrix2T<T> SubMatrixType;
	typedef Vector3T<T> VectorType;
	typedef T ValueType;


	Matrix3T(){	}

	Matrix3T(T m00,T m01,T m02,
			 T m10,T m11,T m12,
			 T m20,T m21,T m22)
	{ 
		Set(m00,m01,m02,
			m10,m11,m12,
			m20,m21,m22);
	}

	Matrix3T(const T* cv)
	{
		Set(cv);
	}

	inline MatrixType& Set(T am00,T am01,T am02,
						   T am10,T am11,T am12,
						   T am20,T am21,T am22)
	{
		m00 = am00; m01 = am01; m02 = am02;
		m10 = am10; m11 = am11;	m12 = am12;
		m20 = am20;	m21 = am21;	m22 = am22;
		return *this;
	}

	//set val from a c array
	inline MatrixType& Set(const T* cv)
	{
		memcpy(c_v,cv,sizeof(T) * NRow * NCol);
		return *this;
	}

	//Determination of Matrix
	T Det()const;

	inline operator ValueType*() { return c_v; }	
	inline operator const ValueType*()const { return c_v; }

	
	//Matrix * Matrix
	MatrixType operator * (const MatrixType& matR)const;	

	//mat *= mat
	MatrixType& operator *= (const MatrixType& matR){
		return	*this = (*this) * matR;		
	}

	//Matrix * val
	inline MatrixType operator*(const T& rVal)const{
		MatrixType ret = *this;
		return ret*=rVal;		
	}

	//mat *= val
	MatrixType& operator*= (const T& rval)const{
		for(size_t i =0;i<NRow*NCol ;++i)
			c_v[i] *= rVal;
		return *this;
	}

	//mat += mat
	MatrixType& operator+=(const MatrixType& rMat){
		for(size_t i =0;i<NRow*NCol ;++i)
			c_v[i] += rMat.c_v[i];
		return *this;
	}	
	//mat -= mat;
	MatrixType& operator-=(const MatrixType& rMat){
		for(size_t i =0;i<NRow*NCol ;++i)
			c_v[i] -= rMat.c_v[i];
		return *this;
	}

	//Mat - mat;
	MatrixType operator-(const MatrixType rMat)const{
		MatrixType ret = *this;
		return ret -= rMat;
	}

	//mat + mat
	inline MatrixType operator+(const MatrixType& rMat)const{
		MatrixType ret = *this;
		return ret+= rMat;
	}

	//mat ^ p
	inline MatrixType operator^(int p)const;	

	//mat * v
	inline VectorType operator*(const VectorType& vec)const
	{
		return VectorType( RowVector(0).Dot(vec), RowVector(1).Dot(vec),RowVector(2).Dot(vec) );
	}

	//SubMatrix of [r,c];
	SubMatrixType GetSubMatrix(int r, int c)const;
	//
	VectorType RowVector(size_t r)const
	{
		return VectorType( (*this)(r,0),(*this)(r,1),(*this)(r,2) );
	}
	VectorType ColVector(size_t c)const
	{
		return VectorType( (*this)(0,c),(*this)(1,c),(*this)(2,c) );
	}

	//
	void SetRow(size_t r, const VectorType& RVec)
	{
		for(size_t i=0;i<NCol;++i)
			(*this)(r,i) = RVec.c_v[i];
	}
	//
	void SetCol(size_t c, const VectorType& CVec)
	{
		for(size_t i=0;i<NRow;++i)
			(*this)(i,c) = CVec.c_v[i];
	}


	//
	T& operator()(size_t r,size_t c)
	{ 
		assert(r<NRow && c<NCol);			
		return c_v[r*NCol+c]; 
	}
	const T& operator()(size_t r,size_t c)const
	{ 
		assert(r<NRow && c<NCol);		
		return c_v[r*NCol+c]; 
	}

	//
	inline MatrixType GetTranspose()const{
		MatrixType transMat;
		transpose(transMat, *this);
		return transMat;
	}
	//
	MatrixType& SelfTranspose(){
		transpose(*this, *this);
	}
	//
	inline MatrixType GetInverse()const{
		MatrixType ret = ZERO;
		inverse(ret, *this);
		return ret;		
	}

	//UnitTest
	static void UnitTest();

public:

	const static size_t NRow = 3;
	const static size_t NCol = 3;
	
	union{
		struct{
			T m00,m01,m02;
			T m10,m11,m12;
			T m20,m21,m22;
		};
		T c_v[NRow*NCol];
	};	

	const static MatrixType IDENTITY;
	const static MatrixType ZERO;

	
};




//transpose src to dest
template<typename T>
void transpose(Matrix3T<T>& dest, const Matrix3T<T>& src)
{	
	if(&dest != &src)
	{
		dest.m01 = src.m10;
		dest.m02 = src.m20;		
		dest.m10 = src.m01;
		dest.m12 = src.m21;		
		dest.m20 = src.m02;
		dest.m21 = src.m12;		

		dest.m00 = src.m00;
		dest.m11 = src.m11;
		dest.m22 = src.m22;
	}
	else
	{
		std::swap(dest.m01, dest.m10);
		std::swap(dest.m02, dest.m20);			
		std::swap(dest.m12, dest.m21);		
	}
}

//inverse src to dest 
template<typename T> 
bool inverse(Matrix3T<T>& dest, const Matrix3T<T>& src);

//global I/O
template<typename T>
std::ostream& operator<<(std::ostream& os, Matrix3T<T> mat);

template<typename T>
std::istream& operator>>(std::istream& is, Matrix3T<T>& mat);



END_NAMESPACE_OXGE